home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / games / IndiZone / sw / main.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  24.4 KB  |  985 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <stream.h>
  20. #include <sys/types.h>
  21. #include <sys/time.h>
  22. #include "sw.h"
  23. #include "extern.h"
  24. #include "main.h"
  25. #include "sw_comm.h"
  26. #include "control.h"
  27. #include "display.h"
  28. #include "explode.h"
  29. #include "hud.h"
  30. #include "ship.h"
  31. #include "universe.h"
  32. #include "ship_regular.h"
  33. #include "sound.h"
  34. #include <Inventor/SoSensor.h>
  35.  
  36. static SoOneShotSensor*    goingIdle;        // frameAdvance sensor
  37. static struct timeval    timeStart,        // to find time step
  38.             timeEnd,        // to find time step
  39.             fpsStart;        // start time to find fps
  40. static struct timezone    tz;
  41.  
  42. ShipObject*        myShip;            // pointer to my ship
  43. int            buttonPressed[3];    // mouse button states
  44.  
  45. // since we're using transparent stuff our Inventor callbacks get called
  46. // multiple times for each frame.  I can't find a way to find out which
  47. // pass is being drawn from Inventor, so frameNumber will tell us when
  48. // we should actually draw the stars and hud.  The number actually just
  49. // cycles from 0 to some small number (it's also used to calculate frames
  50. // per second).
  51. int            frameNumber;
  52.  
  53. // too be sure we don't advance the frame until something has actually
  54. // been drawn, frameDrawn is set when the stars are drawn.
  55. int            frameDrawn;
  56.  
  57. // view screen positions and visibility of every object
  58. ObjectView        playerView[MAXPLAYERS][MAXMISSILES+1];
  59. ObjectView        asteroidView[MAXASTEROIDS];
  60. ObjectView        flagView[NUMTEAMS];
  61. ObjectView        missileLock;
  62. int            wasFlagCaptured;    // TRUE if my flag just captured
  63.  
  64. // bunch 'o useful stuff here
  65. static ShipObject*    shipList[MAXPLAYERS];    // list of all ships
  66. static ShipObject*    targetList[MAXPLAYERS];    // list of all targets
  67. static int        targetPlayer[MAXPLAYERS];// target player number
  68. static int        numPlayers;        // number of players
  69. static int        numTargets;        // number of available targets
  70. static int        target;            // current target number
  71. static Weapon        weaponState;        // current weapon
  72. static int        fps;            // current frames per second
  73. static int        showFps = FALSE;    // TRUE if fps displayed
  74. static int        rangeState;        // current range setting
  75. static int        done = FALSE;        // quit flag
  76. static int        paused = FALSE;        // paused flag
  77. static int        pausedOnIconify = FALSE;// paused due to iconified flag
  78. static XtIntervalId    pausedTimeout;
  79. static SbVec2s        viewSize;        // view screen size
  80. static float        cameraCotan;        // -1.0/tan(camera angle)
  81. static float        cameraAspect;        // camera aspect ratio
  82. static SwServer*    gameServer;        // pointer to server
  83. static TeamInfo        teamStatus[NUMTEAMS];    // team information
  84. static Active        oldActive;        // active state before pausing
  85.  
  86. // radar ranges are stored here (in meters)
  87. #define    NUMRADARRANGES    3
  88. static float        range[NUMRADARRANGES] = { 1000.0, 4000.0, 10000.0 };
  89.  
  90. static void        pausedWakeup(XtPointer, XtIntervalId*)
  91. {
  92.   // reset wakeup call
  93.   pausedTimeout = XtAppAddTimeOut(SoXt::getAppContext(), 1000,
  94.                             pausedWakeup, 0);
  95.  
  96.   handleServerMessages();
  97.   handleBroadcastMessages();
  98.   sendBroadcast(myShip->shipInfo());
  99. }
  100.  
  101. static void        initializeSelf()
  102. {
  103.   myShip->reset();
  104.   if (wasFlagCaptured) {
  105.     float* bp = basePosition(myShip->team());
  106.     float p[3];
  107.     do {
  108.       p[0] = drand48() * 2.0 - 1.0;
  109.       p[1] = drand48() * 2.0 - 1.0;
  110.       p[2] = drand48() * 2.0 - 1.0;
  111.     } while (p[0] * p[0] + p[1] * p[1] + p[2] * p[2] > 1.0);
  112.     p[0] = bp[0] + 0.9 * BASERADIUS * p[0];
  113.     p[1] = bp[1] + 0.9 * BASERADIUS * p[1];
  114.     p[2] = bp[2] + 0.9 * BASERADIUS * p[2];
  115.     myShip->position(SbVec3f(p));
  116.     wasFlagCaptured = FALSE;
  117.   }
  118.   else {
  119.     float p[3];
  120.     do {
  121.       p[0] = 0.8 * FIELDSIZE * (drand48() * 2.0 - 1.0);
  122.       p[1] = 0.8 * FIELDSIZE * (drand48() * 2.0 - 1.0);
  123.       p[2] = 0.8 * FIELDSIZE * (drand48() * 2.0 - 1.0);
  124.     } while (hitAsteroid(p, 2.0*myShip->shipBound()) != -1);
  125.     myShip->position(SbVec3f(p));
  126.   }
  127.   myShip->orientation(SbRotation(SbVec3f(drand48() * 2.0 - 1.0,
  128.                     drand48() * 2.0 - 1.0,
  129.                     drand48() * 2.0 - 1.0),
  130.                     2.0 * M_PI * drand48()));
  131. }
  132.  
  133. static void        findTargets()
  134. {
  135.   ShipObject* oldTarget = (target == -1) ? NULL : targetList[target];
  136.   target = -1;
  137.   numTargets = 0;
  138.   if (myShip->active() == ObjectInactive ||  myShip->exploding()) return;
  139.   for (int i = 1; i < numPlayers; i++) {
  140.     if (shipList[i]->active() == ObjectActive && !shipList[i]->exploding()) {
  141.       targetList[numTargets] = shipList[i];
  142.       targetPlayer[numTargets] = i;
  143.       if (targetList[numTargets] == oldTarget) target = numTargets;
  144.       numTargets++;
  145.     }
  146.   }
  147.   if (oldTarget && target == -1) targetChanged();
  148. }
  149.  
  150. static int        computeVisibility(ObjectView& ov, float r)
  151. {
  152.   if (ov.lp[2] >= 0.0) {
  153.     ov.visible = FALSE;
  154.     return FALSE;
  155.   }
  156.   float s = 0.5 * cameraCotan / ov.lp[2];
  157.   ov.p[0] = (short)(0.5 + (float)viewSize[0] * (s * ov.lp[0]/cameraAspect+0.5));
  158.   ov.p[1] = (short)(0.5 + (float)viewSize[1] * (s * ov.lp[1] + 0.5));
  159.   ov.r = (short)(0.5 + (float)viewSize[1] * s * r);
  160.   ov.visible = (ov.p[0] >= -ov.r && ov.p[0] <= viewSize[0] + ov.r &&
  161.         ov.p[1] >= -ov.r && ov.p[1] <= viewSize[1] + ov.r);
  162.   return ov.visible;
  163. }
  164.  
  165. static void        findVisibility()
  166. {
  167.   // FIXME -- fix sticky sizes for explosions (only stick if big and do
  168.   //        correct visibility for the sticky size)
  169.   for (int i = 0; i < MAXPLAYERS; i++) {
  170.     ShipObject* s = shipList[i];
  171.     if (!s) continue;
  172.     ShipInfo& si = s->shipInfo();
  173.     if (!self(s)) {
  174.       if (s->active() != ObjectActive) {
  175.     playerView[i][0].visible = FALSE;
  176.     continue;
  177.       }
  178.       myShip->findLocalPosition(si.info.position, playerView[i][0].lp);
  179.       if (si.info.explodeTime == 0.0) {
  180.     computeVisibility(playerView[i][0], s->shipBound());
  181.     playerView[i][0].exploding = FALSE;
  182.     s->shipVisibility(playerView[i][0].visible);
  183.       }
  184.       else {
  185.         short oldSize = playerView[i][0].exploding ? playerView[i][0].r : -1;
  186.     computeVisibility(playerView[i][0], 2.0 * s->shipBound());
  187.     playerView[i][0].exploding = TRUE;
  188.     s->shipVisibility(FALSE);
  189.  
  190.     // make explosion sizes sticky
  191.     if (oldSize != -1) playerView[i][0].r = oldSize;
  192.       }
  193.     }
  194.     else {
  195.       playerView[i][0].visible = FALSE;
  196.       if (s->active() == ObjectInactive) continue;
  197.     }
  198.  
  199.     for (int j = 0; j < MAXMISSILES; j++) {
  200.       if (si.missile[j].active != ObjectActive) continue;
  201.       myShip->findLocalPosition(si.missile[j].position, playerView[i][j+1].lp);
  202.       if (si.missile[j].explodeTime == 0.0) {
  203.     computeVisibility(playerView[i][j+1], s->missileBound());
  204.     playerView[i][j+1].exploding = FALSE;
  205.     s->missileVisibility(j, playerView[i][j+1].visible);
  206.       }
  207.       else {
  208.         short oldSize = playerView[i][j+1].exploding? playerView[i][j+1].r: -1;
  209.     computeVisibility(playerView[i][j+1], 2.5 * s->missileBound());
  210.     playerView[i][j+1].exploding = TRUE;
  211.     s->missileVisibility(j, FALSE);
  212.  
  213.     // make explosion sizes sticky
  214.     if (oldSize != -1) playerView[i][j+1].r = oldSize;
  215.       }
  216.     }
  217.   }
  218.  
  219.   // find asteroid visibility
  220.   for (i = 0; i < numberAsteroids(); i++) {
  221.     myShip->findLocalPosition(asteroidPosition(i), asteroidView[i].lp);
  222.     int v = computeVisibility(asteroidView[i], asteroidRadius(i));
  223.     if (!v) asteroidVisibility(i, -1);
  224.     else if (asteroidView[i].r > 10) asteroidVisibility(i, 0);
  225.     else asteroidVisibility(i, 1);
  226.   }
  227.  
  228.   // find flag visibility
  229.   for (i = 0; i < NUMTEAMS; i++) {
  230.     if (teamStatus[i].state == FlagReady) {
  231.       myShip->findLocalPosition(teamStatus[i].position, flagView[i].lp);
  232.       flagVisibility(Team(i), computeVisibility(flagView[i], 1.75 * FLAGSIZE));
  233.     }
  234.   }
  235. }
  236.  
  237. static void        forceInventorRedraw()
  238. {
  239.   // touch camera node so that Inventor redraws
  240.   camera->position = camera->position;
  241. }
  242.  
  243. static void        reshapeViewScreen()
  244. {
  245.   viewSize = view->getSize();
  246.  
  247.   // get constants for a particular shape
  248.   cameraCotan = -1.0 / tan(camera->heightAngle.getValue() / 2.0);
  249.   cameraAspect = camera->aspectRatio.getValue();
  250. }
  251.  
  252. static double        timeDiff(const struct timeval* t2,
  253.                     const struct timeval* t1)
  254. {
  255.   return (double)((long)(t2->tv_sec - t1->tv_sec)) +
  256.     (double)(t2->tv_usec - t1->tv_usec) / 1000000.0;
  257. }
  258.  
  259. static void        frameAdvance(void*, SoSensor*)
  260. {
  261.   static float inBaseTime = 0.0;
  262.  
  263.   // get time step between frames
  264.   gettimeofday(&timeEnd, &tz);
  265.   float dt = timeDiff(&timeEnd, &timeStart);
  266.   timeStart = timeEnd;
  267.  
  268.   if (!paused) {
  269.     goingIdle->schedule();
  270.     if (!frameDrawn) return;
  271.     frameDrawn = FALSE;
  272.     frameNumber++;
  273.  
  274.     handleServerMessages();
  275.     handleBroadcastMessages();
  276.  
  277.     // get frames per second every second or every 2 frames (whichever longer)
  278.     if (frameNumber >= 2) {
  279.       float dtFps = timeDiff(&timeEnd, &fpsStart);
  280.       if (dtFps >= 1.0) {
  281.     fpsStart = timeStart;
  282.     fps = int(float(frameNumber) / dtFps + 0.5);
  283.     frameNumber = 0;
  284.       }
  285.     }
  286.  
  287.     // advance my ship in time
  288.     myShip->advance(dt);
  289.     float* p = myShip->shipInfo().info.position;
  290.  
  291.     if (myShip->active() == ObjectActive && !myShip->exploding()) {
  292.  
  293.       // check if I'm on any flags
  294.       if (myShip->flag() == NoTeam) {
  295.     float dx, dy, dz;
  296.     for (int i = 0; i < NUMTEAMS; i++)
  297.       if (teamStatus[i].state == FlagReady) {
  298.         dx = teamStatus[i].position[0] - p[0];
  299.         dy = teamStatus[i].position[1] - p[1];
  300.         dz = teamStatus[i].position[2] - p[2];
  301.         if (dx*dx + dy*dy + dz*dz <=
  302.         (myShip->shipBound()+FLAGSIZE)*(myShip->shipBound()+FLAGSIZE))
  303.           myShip->grabFlag(Team(i));
  304.       }
  305.       }
  306.  
  307.       // check if I'm in a team base
  308.       Team whichBase = insideBase(myShip->shipInfo().info.position,
  309.                             myShip->shipBound());
  310.       if (whichBase != NoTeam) {
  311.     Team f = myShip->flag(), t = myShip->team();
  312.     if (f != NoTeam) {            // maybe a capture
  313.       if ((whichBase != t && f == t) ||    // my flag in enemy base
  314.         (whichBase == t && f != t)) {    // enemy flag in my base
  315.         SwCaptureFlagMessage m;
  316.         m.flagTeam = f;
  317.         m.captorTeam = t;
  318.         server()->send(m);
  319.       }
  320.     }
  321.     if (whichBase == t) {            // in my base, resupply me
  322.       inBaseTime += dt;
  323.  
  324.       // increase missiles
  325.       int s = int(inBaseTime);
  326.       inBaseTime -= float(s);
  327.       if (s > 0 && myShip->numMissiles() < myShip->maxMissiles()) {
  328.         myShip->numMissiles(myShip->numMissiles() + s);
  329.         missileChanged();
  330.       }
  331.  
  332.       // increase fuel
  333.       if (myShip->fuelLeft() < 1.0) {
  334.         myShip->fuelLeft(myShip->fuelLeft() + 0.5*dt*myShip->fuelRate());
  335.         fuelChanged();
  336.       }
  337.  
  338.       // increase shields
  339.       myShip->shieldStrength(0.01*dt);
  340.       shieldsChanged();
  341.     }
  342.       }
  343.       else inBaseTime = 0.0;
  344.  
  345.       // see if I hit an asteroid
  346.       int a  = hitAsteroid(p, myShip->shipBound());
  347.       if (a != -1) {
  348.     // move me outside of asteroid and make me stationary
  349.     float* ap = asteroidPosition(a);
  350.     SbVec3f sp(p[0] - ap[0], p[1] - ap[1], p[2] - ap[2]);
  351.     sp.normalize();
  352.     sp *= myShip->shipBound() + asteroidRadius(a);
  353.     sp += SbVec3f(ap);
  354.     myShip->position(sp);
  355.     myShip->velocity(SbVec3f(0.0, 0.0, 0.0));
  356.  
  357.     // blow me up
  358.     myShip->shipInfo().lost++;
  359.     myShip->explodeShip(NetId());
  360.     server()->send("hit an asteroid");
  361.       }
  362.  
  363.     }
  364.  
  365.     // broadcast my ship's data
  366.     sendBroadcast(myShip->shipInfo());
  367.  
  368.     // find available targets
  369.     findTargets();
  370.  
  371.     // advance things in the universe
  372.     universeAdvance(dt);
  373.  
  374.     // find visibility state for all active objects
  375.     findVisibility();
  376.  
  377.     // move missile lock box towards target if:
  378.     //        missiles are the selected weapon
  379.     //        a missile is ready
  380.     //        a target is selected
  381.     //        target is not exploding
  382.     //        and target is on screen
  383.     if (target != -1 && weaponState == Missile && 
  384.         playerView[targetPlayer[target]][0].visible &&
  385.         !playerView[targetPlayer[target]][0].exploding &&
  386.         myShip->missileReady()) {
  387.       ObjectView& o = playerView[targetPlayer[target]][0];
  388.       float dx = float((o.p[0] - (viewSize[0]>>1)) - missileLock.p[0]),
  389.         dy = float((o.p[1] - (viewSize[1]>>1)) - missileLock.p[1]);
  390.       float d = sqrt(dx * dx + dy * dy);
  391.       if (d < dt*250.0) {
  392.     missileLock.p[0] = o.p[0] - (viewSize[0]>>1);
  393.     missileLock.p[1] = o.p[1] - (viewSize[1]>>1);
  394.     if (missileLock.r != 16) {
  395.       missileLock.r = 16;
  396.       soundPlay(LockOnSound);
  397.     }
  398.       }
  399.       else {
  400.     missileLock.p[0] = short(missileLock.p[0] + dx*dt*250.0/d);
  401.     missileLock.p[1] = short(missileLock.p[1] + dy*dt*250.0/d);
  402.     missileLock.r = 8;
  403.     soundPlay(SeekingSound);
  404.       }
  405.       missileLock.visible = TRUE;
  406.     }
  407.     else {
  408.       missileLock.visible = FALSE;
  409.       missileLock.r = 0;
  410.     }
  411.  
  412.     // show radar and whatever else needs to be drawn in the control panel
  413.     controlPanelAdvance(dt);
  414.  
  415.     // advance sound (play it)
  416.     soundAdvance(dt);
  417.  
  418.     // make inventor show next frame even if nothing changed
  419.     forceInventorRedraw();
  420.   }
  421. }
  422.  
  423. static void        prepareForFirstFrame()
  424. {
  425.   // for frameAdvance() will get called
  426.   goingIdle->schedule();
  427.  
  428.   // skip any time we've been asleep
  429.   gettimeofday(&timeStart, &tz);
  430.   fpsStart = timeStart;
  431.   fps = 0;
  432.  
  433.   // reset frame count
  434.   frameNumber = 0;
  435.   frameDrawn = FALSE;
  436.  
  437.   // make Inventor show frame
  438.   forceInventorRedraw();
  439. }
  440.  
  441. static int        handleGlobalEvents(XEvent* e)
  442. {
  443.   switch (e->xany.type) {
  444.     case KeyPress:
  445.     case KeyRelease:
  446.     return keyEvent(e);
  447.     case ButtonPress:
  448.     case ButtonRelease: {
  449.     int b;
  450.     switch (e->xbutton.button) {
  451.       case Button1:
  452.         b = LeftMouseButton;
  453.         break;
  454.       case Button2:
  455.         b = MiddleMouseButton;
  456.         break;
  457.       case Button3:
  458.         b = RightMouseButton;
  459.         break;
  460.     }
  461.     buttonPressed[b] = (e->xany.type == ButtonPress);
  462.     break;                    // allow others to use event
  463.       }
  464.   }
  465.   return FALSE;
  466. }
  467.  
  468. static void        usage(const char* pname)
  469. {
  470.   cerr << "usage: " << pname << " -n callsign -s server teamname\n";
  471.   cerr << "\tteamname may be r, g, b, or p.\n";
  472.   exit(1);
  473. }
  474.  
  475. static char*        callsign = NULL;
  476. static char*        serverName = NULL;
  477. static Team        myTeam = NoTeam;
  478.  
  479. static void        parse(int argc, char** argv)
  480. {
  481.   for (int i = 1; i < argc; i++) {
  482.     if (argv[i][0] == '-') switch(argv[i][1]) {
  483.       case 'n':
  484.     if (callsign) usage(argv[0]);
  485.     callsign = argv[++i];
  486.     break;
  487.       case 's':
  488.     if (serverName) usage(argv[0]);
  489.     serverName = argv[++i];
  490.     break;
  491.       default:
  492.     usage(argv[0]);
  493.     }
  494.     else {
  495.       switch (argv[i][0]) {
  496.     case 'r':
  497.     case 'R':
  498.       myTeam = RedTeam;
  499.       break;
  500.     case 'g':
  501.     case 'G':
  502.       myTeam = GreenTeam;
  503.       break;
  504.     case 'b':
  505.     case 'B':
  506.       myTeam = BlueTeam;
  507.       break;
  508.     case 'p':
  509.     case 'P':
  510.       myTeam = PurpleTeam;
  511.       break;
  512.     default:
  513.       usage(argv[0]);
  514.       }
  515.     }
  516.   }
  517.   if (myTeam == NoTeam) usage(argv[0]);
  518.   if (!callsign || !serverName) usage(argv[0]);
  519. }
  520.  
  521. int            main(int argc, char** argv)
  522. {
  523.   int i;
  524.  
  525.   // initialize random number generator
  526.   srand48(time(NULL));
  527.  
  528.   parse(argc, argv);
  529.  
  530.   // make server connection object
  531.   gameServer = new SwServer(serverName);
  532.   if (gameServer->state() != SwServer::NotConnected) {
  533.     cerr << "can't connect to server\n";
  534.     return 1;
  535.   }
  536.  
  537.   // open broadcast socket
  538.   if (!openBroadcast(BROADCASTPORT)) {
  539.     cerr << "can't open broadcast port\n";
  540.     return 1;
  541.   }
  542.  
  543.   makeDisplay(argv[0]);                // make render areas
  544.   // FIXME -- make sure makeDisplay() worked
  545.  
  546.   // make universe (sun, stars, places for ships, etc.)
  547.   makeUniverse();
  548.  
  549.   // we have some callbacks in the graph and Inventor doesn't seem to
  550.   // like to cache bounding boxes when they're there.
  551.   universe->setBoundingBoxCaching(FALSE);
  552.  
  553.   // view universe (with transparent things correctly)
  554.   view->setSceneGraph(universe);
  555.   view->setTransparencyType(SoGLRenderAction::DELAYED_ADD);
  556.  
  557.   // create head's up display
  558.   makeHud();
  559.  
  560.   // set sensor to fire whenever Inventor is done with it's stuff
  561.   goingIdle = new SoOneShotSensor(frameAdvance, NULL);
  562.  
  563.   // read explosion data
  564.   if (!readExplosion(explosionDirectory())) {
  565.     cerr << "can't read explosion movie\n";
  566.     return 1;                    // quit with error code
  567.   }
  568.  
  569.   // open audio port
  570.   openSound(soundsDirectory());
  571.  
  572.   // initialize global info
  573.   for (i = 0; i < NUMTEAMS; i++) {        // initialize teams
  574.     teamStatus[i].team = Team(i);
  575.     teamStatus[i].players = 0;
  576.     teamStatus[i].won = 0;
  577.     teamStatus[i].lost = 0;
  578.     teamStatus[i].state = FlagNoExist;
  579.   }
  580.   numPlayers = 0;
  581.   for (i = 0; i < MAXPLAYERS; i++) shipList[i] = NULL;
  582.   numTargets = 0;
  583.   target = -1;
  584.   for (i = 0; i < MAXPLAYERS; i++) targetList[i] = NULL;
  585.   rangeState = 1;
  586.   weaponState = Laser;
  587.   wasFlagCaptured = TRUE;            // start me in my base
  588.   missileLock.visible = FALSE;
  589.   missileLock.p[0] = 0;
  590.   missileLock.p[1] = 0;
  591.  
  592.   // make my ship and add it to universe
  593.   myShip = new RegularShip(server()->hostId(), myTeam, callsign);
  594.   initializeSelf();
  595.   myShip->active(ObjectInactive);
  596.   addPlayer(myShip);
  597.   wasFlagCaptured = TRUE;            // start me in my base again
  598.  
  599.   // connect to server
  600.   if (!server()->open(myShip)) {
  601.     cerr << "cannot connect to server\n";
  602.     return 1;                    // quit with error code
  603.   }
  604.  
  605.   // realize everything and display the window
  606.   SoXt::show(mainWindow);
  607.   reshapeViewScreen();
  608.  
  609.   // reset various stuff (after window open cos they might draw stuff)
  610.   hudReset();
  611.   controlPanelReset();
  612.  
  613.   // tell everyone we're here
  614.   server()->send("joining as a ship");
  615.  
  616.   // no mouse buttons down
  617.   for (i = int(LeftMouseButton); i <= int(RightMouseButton); i++)
  618.     buttonPressed[i] = FALSE;
  619.  
  620.   if (hasSound()) soundDial(SoundAll);
  621.  
  622.   prepareForFirstFrame();
  623.  
  624.   // Main event loop
  625.   //    Get event and dispatch it.  Also check for events we're always
  626.   //    interested in knowing about.  This includes keyboard events because
  627.   //    the keyboard has the same effect everywhere.
  628.   //    It's a good thing we don't need to pass extension events to Inventor!
  629.   XEvent event;
  630.   XtAppContext context = SoXt::getAppContext();
  631.   while (!done) {
  632.     if (server()->state() != SwServer::Connected) {
  633.       cerr << "unexpected loss of server connection -- terminating\n";
  634.       break;                    // lost my server connection
  635.     }
  636.  
  637.     XtAppNextEvent(context, &event);
  638.     if (!handleGlobalEvents(&event))        // see if I want it
  639.       XtDispatchEvent(&event);            // dispatch it if I don't
  640.  
  641.     // check for window resizing
  642.     if (event.xany.type == ConfigureNotify) {
  643.       XConfigureEvent* ce = (XConfigureEvent*)&event;
  644.       if (ce->window == XtWindow(view->getWidget()) &&
  645.         ce->width != viewSize[0] || ce->height != viewSize[1])
  646.     reshapeViewScreen();
  647.     }
  648.  
  649.     // check for window mapping (un-iconfiy falls in this category)
  650.     else if (event.xany.type == MapNotify &&
  651.                 event.xmap.window == XtWindow(mainWindow)) {
  652.       if (pausedOnIconify) {
  653.     pausedOnIconify = FALSE;
  654.     resumeGame();
  655.       }
  656.       frameDrawn = FALSE;
  657.       frameNumber++;
  658.       forceInventorRedraw();
  659.     }
  660.  
  661.     // check for window unmapping (iconfiy falls in this category)
  662.     else if (event.xany.type == UnmapNotify &&
  663.                 event.xunmap.window == XtWindow(mainWindow)) {
  664.       if (!paused) {
  665.     pausedOnIconify = TRUE;
  666.     pauseGame();
  667.       }
  668.     }
  669.   }
  670.  
  671.   // drop any flag I have
  672.   myShip->dropFlag();
  673.  
  674.   // tell everyone we're leaving
  675.   server()->send("signing off");
  676.  
  677.   // close server connection
  678.   delete gameServer;
  679.  
  680.   // close broadcast connection
  681.   closeBroadcast();
  682.  
  683.   // close audio port
  684.   closeSound();
  685.  
  686.   // delete all the players
  687.   for (i = 0; i < MAXPLAYERS; i++)
  688.     delete removePlayer(i);
  689.  
  690.   // delete other stuff
  691.   deleteUniverse();
  692.  
  693.   return 0;
  694. }
  695.  
  696. int            self(const ShipObject* o)
  697. {
  698.   return (o->id() == myShip->id());
  699. }
  700.  
  701. int            self(InAddr a)
  702. {
  703.   return (NetId(a) == myShip->id());
  704. }
  705.  
  706. int            self(NetId id)
  707. {
  708.   return (id == myShip->id());
  709. }
  710.  
  711. void            restartSelf()
  712. {
  713.   initializeSelf();
  714.   controlPanelReset();
  715.   SwAliveMessage m;
  716.   server()->send(m);
  717. }
  718.  
  719. SwServer*        server()
  720. {
  721.   return gameServer;
  722. }
  723.  
  724. int            numberPlayers()
  725. {
  726.   return numPlayers;
  727. }
  728.  
  729. ShipObject*        getPlayer(int i)
  730. {
  731.   if (i < 0 || i >= MAXPLAYERS) return NULL;
  732.   return shipList[i];
  733. }
  734.  
  735. int            addPlayer(ShipObject* s)
  736. {
  737.   if (numPlayers >= MAXPLAYERS) return FALSE;    // no room!
  738.   for (int i = 0; i < MAXPLAYERS; i++)        // scan list
  739.     if (shipList[i] == NULL) {            //  for an empty slot
  740.       shipList[i] = s;                //   fill it in
  741.       numPlayers++;                //   one more player
  742.       ships->insertChild(s->root(), 0);        //   add ship to scene graph
  743.       playersChanged();                //   show new player count
  744.       return TRUE;                //   successful add
  745.     }
  746.   return FALSE;
  747. }
  748.  
  749. ShipObject*        removePlayer(int num)
  750. {
  751.   ShipObject* s = shipList[num];
  752.   if (s == NULL) return s;            // no such player
  753.   ships->removeChild(s->root());        // remove from scene graph
  754.   shipList[num] = NULL;                // make slot empty
  755.   numPlayers--;                    // one less player
  756.   return s;                    // successful remove
  757. }
  758.  
  759. int            lookupPlayer(NetId id)
  760. {
  761.   for (int i = 0; i < MAXPLAYERS; i++)        // search all slots
  762.     if (shipList[i] && shipList[i]->id() == id)    //  if id's match
  763.       return i;                    //   found it
  764.   return -1;                    // didn't find it
  765. }
  766.  
  767. TeamInfo&        getTeam(Team t)
  768. {
  769.   return teamStatus[int(t)];
  770. }
  771.  
  772. void            setTeam(const TeamInfo& info)
  773. {
  774.   Team t = info.team;
  775.   TeamInfo& ti = teamStatus[int(t)];
  776.   int oldState = ti.state;
  777.   ti = info;
  778.   switch (ti.state) {
  779.     case FlagNoExist:                // flag is gone now
  780.     if (oldState != ti.state)
  781.       flagInSpace(t, FALSE);
  782.     if (t == myShip->flag()) {        // I used to have it
  783.       myShip->flag(NoTeam);            // I have nothing now
  784.       flagChanged();
  785.     }
  786.     break;
  787.     case FlagReady:
  788.     if (oldState != ti.state)
  789.       flagInSpace(t, TRUE);
  790.     break;
  791.     case FlagOnShip:
  792.     if (oldState != ti.state)
  793.       flagInSpace(t, FALSE);
  794.     break;
  795.   }
  796. }
  797.  
  798. int            numberTargets()
  799. {
  800.   return numTargets;
  801. }
  802.  
  803. int            currentTarget()
  804. {
  805.   return target;
  806. }
  807.  
  808. ShipObject*        getTarget(int t)
  809. {
  810.   return targetList[t];
  811. }
  812.  
  813. void            nextTarget()
  814. {
  815.   if (numTargets == 0) return;
  816.   if (++target >= numTargets) target = -1;
  817.   targetChanged();
  818. }
  819.  
  820. void            prevTarget()
  821. {
  822.   if (numTargets == 0) return;
  823.   if (--target < -1) target = numTargets - 1;
  824.   targetChanged();
  825. }
  826.  
  827. void            noTarget()
  828. {
  829.   if (target == -1) return;
  830.   target = -1;
  831.   targetChanged();
  832. }
  833.  
  834. float            radarRange()
  835. {
  836.   return range[rangeState];
  837. }
  838.  
  839. void            increaseRange()
  840. {
  841.   if (rangeState >= NUMRADARRANGES-1) return;
  842.   rangeState++;
  843. }
  844.  
  845. void            decreaseRange()
  846. {
  847.   if (rangeState < 1) return;
  848.   rangeState--;
  849. }
  850.  
  851. Weapon            currentWeapon()
  852. {
  853.   return weaponState;
  854. }
  855.  
  856. void            currentWeapon(Weapon w)
  857. {
  858.   if (weaponState != w) {
  859.     weaponState = w;
  860.     weaponChanged();
  861.   }
  862. }
  863.  
  864. void            toggleFps()
  865. {
  866.   showFps = !showFps;
  867. }
  868.  
  869. int            isFpsOn()
  870. {
  871.   return showFps;
  872. }
  873.  
  874. int            getFps()
  875. {
  876.   return fps;
  877. }
  878.  
  879. SbVec2s&        viewScreenSize()
  880. {
  881.   return viewSize;
  882. }
  883.  
  884. void            getViewDirection(const SbVec2s& p, SbVec3f& v)
  885. {
  886.   float x = (float)p[0] / (float)viewSize[0] * 2.0 - 1.0,
  887.     y = (float)p[1] / (float)viewSize[1] * 2.0 - 1.0,
  888.     z = cameraCotan;
  889.   v.setValue(x * cameraAspect, y, z);
  890.   v.normalize();
  891. }
  892.  
  893. int            getViewPosition(const SbVec3f& v, SbVec2s& p)
  894. {
  895.   if (v[2] != 0.0) {
  896.     float t = 0.5 * cameraCotan / v[2];
  897.     p[0] = (short)(0.5 + (float)viewSize[0] * (t * v[0] / cameraAspect + 0.5));
  898.     p[1] = (short)(0.5 + (float)viewSize[1] * (t * v[1] + 0.5));
  899.     return (t > 0.0);
  900.   }
  901.   else {
  902.     p[0] = p[1] = 0;
  903.     return FALSE;
  904.   }
  905. }
  906.  
  907. void            showHelp()
  908. {
  909.   char buf[256];
  910.   sprintf(buf, "/usr/sbin/showcase -v %s", helpFile());
  911.   pauseGame();
  912.   system(buf);
  913. }
  914.  
  915. void            pauseGame()
  916. {
  917.   if (!paused) togglePauseGame();
  918. }
  919.  
  920. void            resumeGame()
  921. {
  922.   if (paused) togglePauseGame();
  923. }
  924.  
  925. void            togglePauseGame()
  926. {
  927.   paused = !paused;
  928.  
  929.   if (paused) {
  930.     // tell everyone we've paused
  931.     server()->send("Paused");
  932.  
  933.     // we want to send our data every second to let people know we're here
  934.     pausedTimeout = XtAppAddTimeOut(SoXt::getAppContext(), 1000,
  935.                             pausedWakeup, 0);
  936.  
  937.     oldActive = myShip->active();
  938.     myShip->active(ObjectPaused);
  939.     //        send my data every second or so while I'm paused
  940.   }
  941.   else {
  942.     // tell everyone we've resumed
  943.     server()->send("Resumed");
  944.  
  945.     XtRemoveTimeOut(pausedTimeout);
  946.     prepareForFirstFrame();
  947.     handleServerMessages();
  948.     handleBroadcastMessages();
  949.     // flush radio buffers here
  950.     myShip->active(oldActive);
  951.   }
  952. }
  953.  
  954. int            isPaused()
  955. {
  956.   return paused;
  957. }
  958.  
  959. void            quitGame()
  960. {
  961.   done = TRUE;
  962. }
  963.  
  964. ShipObject*        makeShip(ShipClass c, NetId id, Team t, const char* n)
  965. {
  966.   switch (c) {
  967.     case ShipClassRegular:
  968.     return new RegularShip(id, t, n);
  969.   }
  970.   return 0;
  971. }
  972.  
  973. float            explodeVolume(float p[3], float v)
  974. {
  975.   float* o = myShip->shipInfo().info.position;
  976.   float dx = o[0] - p[0],
  977.     dy = o[1] - p[1],
  978.     dz = o[2] - p[2];
  979.   float d = (dx * dx + dy * dy + dz * dz);
  980.   if (d <= BASERADIUS * BASERADIUS) return 1.0;
  981.   d = v * BASERADIUS / sqrt(d);
  982.   if (d > 1.0) return 1.0;
  983.   return d;
  984. }
  985.